package com.ly.heart.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.ly.common.PageParam;
import com.ly.common.core.domain.AjaxResult;
import com.ly.common.core.domain.entity.SysDept;
import com.ly.common.core.domain.entity.SysUser;
import com.ly.common.exception.CustomException;
import com.ly.common.utils.DateUtils;
import com.ly.common.utils.SecurityUtils;
import com.ly.common.utils.StringUtils;
import com.ly.common.utils.erp.*;
import com.ly.heart.domain.*;
import com.ly.heart.domain.dto.KingFinancePayDto;
import com.ly.heart.domain.dto.StartProcessDto;
import com.ly.common.core.domain.ActCurrentTaskVo;
import com.ly.heart.domain.vo.KingFinancePayMenuVo;
import com.ly.heart.domain.vo.KingFinancePayVo;
import com.ly.heart.mapper.KingFinancePayMapper;
import com.ly.heart.service.*;
import com.ly.heart.service.king.PayPushService;
import com.ly.heart.util.ChineseToPinyin;
import com.ly.system.service.ISysDeptService;
import com.ly.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;

/**
 * 金蝶-财务-付款单Service业务层处理
 *
 * @author wutao
 * @date 2024-06-19
 */
@Service
@Slf4j
public class KingFinancePayServiceImpl extends ServiceImpl<KingFinancePayMapper, KingFinancePayEntity> implements IKingFinancePayService {

    @Resource
    private KingFinancePayMapper kingFinancePayEntityMapper;
    @Resource
    private IKingFinancePayDetailService iKingFinancePayDetailService;
    @Resource
    private IKingFinancePayPayeesService iKingFinancePayPayeesService;
    @Resource
    private IKingPayableReceivableVerificationService iKingPayableReceivableVerificationService;
    @Resource
    private IOrderPaymentPlanService iOrderPaymentPlanService;
    @Resource
    private IOrderService iOrderService;
    @Resource
    private IKingInvoicePayableReceivableOtherDetailService iKingInvoicePayableReceivableOtherDetailService;
    @Resource
    private IKingInvoicePayableService iKingInvoicePayableService;
    @Resource
    private IActModelService iActModelService;
    @Resource
    private IKingFinancePayRefundDetailService iKingFinancePayRefundDetailService;
    @Resource
    private ICustomerService iCustomerService;
    @Resource
    private ISupplierService iSupplierService;
    @Resource
    private ISysDeptService iSysDeptService;
    @Resource
    private ISysUserService iSysUserService;
    @Resource
    private IKingBaseTransactionsCompanyService iKingBaseTransactionsCompanyService;
    @Resource
    private IKingBaseBankService iKingBaseBankService;
    @Resource
    private IKingBaseCurrencyService iKingBaseCurrencyService;
    @Resource
    private IActTaskCommentService iActTaskCommentService;
    @Resource
    private PayPushService payPushService;
    @Resource
    private IKingBankAccountService iKingBankAccountService;
    @Resource
    private IKingPayableReceivableVerificationDetailService iKingPayableReceivableVerificationDetailService;

    /**
     * 查询金蝶-财务-付款单
     *
     * @param id 金蝶-财务-付款单ID
     * @return 金蝶-财务-付款单
     */
    @Override
    public KingFinancePayVo selectKingFinancePayEntityById ( String id ) {
        KingFinancePayVo vo = new KingFinancePayVo ();
        KingFinancePayEntity payEntity = kingFinancePayEntityMapper.selectKingFinancePayEntityById ( id );
        String transactionCompanyName = getCompanyName ( payEntity.getTransactionCompanyType (), payEntity.getTransactionCompanyId (), 1 );
        payEntity.setTransactionCompanyName ( transactionCompanyName );
        String receiptCompanyName = getCompanyName ( payEntity.getReceiptDeptType (), payEntity.getReceiptDeptId (), 1 );
        payEntity.setReceiptCompanyName ( receiptCompanyName );
        //查询明细
        KingFinancePayDetailEntity payDetailEntity = new KingFinancePayDetailEntity ();
        payDetailEntity.setRelationId ( id );
        List<KingFinancePayDetailEntity> payReceiptDetailEntities = iKingFinancePayDetailService.selectKingFinancePayReceiptDetailEntityList ( payDetailEntity );
        KingFinancePayPayeesEntity payPayeesEntity = new KingFinancePayPayeesEntity ();
        payPayeesEntity.setPayId ( id );
        List<KingFinancePayPayeesEntity> kingFinancePayPayeesEntities = iKingFinancePayPayeesService.selectKingFinancePayPayeesEntityList ( payPayeesEntity );
        kingFinancePayPayeesEntities.forEach ( payPayees -> {
            String receiptDeptName = getCompanyName ( payPayees.getReceiptDeptType (), payPayees.getReceiptDeptId (), 1 );
            payPayees.setReceiptDeptName ( receiptDeptName );
        } );
        vo.setKingFinancePayEntity ( payEntity ).setKingFinancePayDetailEntities ( payReceiptDetailEntities ).setKingFinancePayPayeesEntities ( kingFinancePayPayeesEntities );
        return vo;
    }

    /**
     * 查询金蝶-财务-付款单列表
     *
     * @param kingFinancePayEntity 金蝶-财务-付款单
     * @return 金蝶-财务-付款单
     */
    @Override
    public List<KingFinancePayEntity> selectKingFinancePayEntityList ( KingFinancePayEntity kingFinancePayEntity ) {
        kingFinancePayEntity.setIsUsed ( ConstUtil.IS_USED_YES );
        kingFinancePayEntity.setPage ( ( kingFinancePayEntity.getPage () - 1 ) * kingFinancePayEntity.getLimit () );
        List<KingFinancePayEntity> kingFinancePayEntities = kingFinancePayEntityMapper.selectKingFinancePayEntityList ( kingFinancePayEntity );
        List<String> ids = kingFinancePayEntities.stream ().map ( KingFinancePayEntity :: getId ).collect ( Collectors.toList () );
        if ( ! CollectionUtils.isEmpty ( ids ) ) {
            // 通过付款单Id查询实际退款金额
            kingFinancePayEntities.forEach ( payEntity -> {
                List<KingFinancePayRefundDetailEntity> kingFinancePayRefundDetailEntities = iKingFinancePayRefundDetailService.queryList ( payEntity.getId () );
                double sum = kingFinancePayRefundDetailEntities.stream ().mapToDouble ( detail -> Double.parseDouble ( detail.getActualRefundMoney () ) ).sum ();
                payEntity.setRefundAmount ( String.valueOf ( sum ) );
                String transactionCompanyName = getCompanyName ( payEntity.getTransactionCompanyType (), payEntity.getTransactionCompanyId (), 1 );
                payEntity.setTransactionCompanyName ( transactionCompanyName );
                String receiptCompanyName = getCompanyName ( payEntity.getReceiptDeptType (), payEntity.getReceiptDeptId (), 1 );
                payEntity.setReceiptCompanyName ( receiptCompanyName );
                // 已退金额=该付款单的付款退款单金额
                List<KingFinancePayRefundDetailEntity> list = iKingFinancePayRefundDetailService.list ( new LambdaQueryWrapper<KingFinancePayRefundDetailEntity> ().
                        eq ( KingFinancePayRefundDetailEntity :: getSourceDetailId, payEntity.getPayDetailId () ) );
                double sumRefundAmount = list.stream ().mapToDouble ( detail -> Double.parseDouble ( StringUtils.isBlank ( detail.getActualRefundMoney () ) ? "0" : detail.getActualRefundMoney () ) ).sum ();
                payEntity.setTotalRefundAmount ( CommonUtil.doublePoint ( sumRefundAmount, 2 ) );
                //处理最后一个审批人
                ActTaskCommentEntity actTaskCommentEntity = iActTaskCommentService.queryLastComment ( payEntity.getInstanceId () );
                if ( actTaskCommentEntity != null ) {
                    payEntity.setApprovedUser ( actTaskCommentEntity.getApprovedUser () );
                    payEntity.setApprovedTime ( actTaskCommentEntity.getCreateTime () );
                    payEntity.setApprovedRemark ( actTaskCommentEntity.getRemark () );
                }
                //任务信息
                ActCurrentTaskVo currentTaskVo = iActModelService.getCurrentTaskByInstanceId ( payEntity.getInstanceId () );
                Optional.ofNullable ( currentTaskVo ).ifPresent ( currentTask -> {
                    payEntity.setCurrentTask ( currentTask );
                } );
            } );
        }
        return kingFinancePayEntities;
    }

    @Override
    public Integer queryCount ( KingFinancePayEntity kingFinancePayEntity ) {
        Integer integer = kingFinancePayEntityMapper.queryCount ( kingFinancePayEntity );
        return integer;
    }

    /**
     * 付款退款单选单
     * 筛选已核销的，核销金额大于0的可以出现，并且只能下推未核销的金额部分，超过未核销部分的金额校验失败
     * 计算可下推金额=如果付款单源单是应付单，则计算未校验金额；否则直接拿付款单的价税合计
     * @param kingFinancePayEntity
     * @return
     */
    @Override
    public AjaxResult queryList ( KingFinancePayEntity kingFinancePayEntity ) {
        kingFinancePayEntity.setPage ( ( kingFinancePayEntity.getPage () - 1 ) * kingFinancePayEntity.getLimit () );
        List<KingFinancePayMenuVo> kingFinancePayMenuVos = kingFinancePayEntityMapper.queryList ( kingFinancePayEntity );
        List<KingFinancePayMenuVo> payMenuVos = new ArrayList<> ();
        kingFinancePayMenuVos.forEach ( vo -> {
            String transactionCompanyName = getCompanyName ( vo.getTransactionCompanyType (), vo.getTransactionCompanyId (), 1 );
            vo.setTransactionCompanyName ( transactionCompanyName );
            String receiptCompanyName = getCompanyName ( vo.getReceiptDeptType (), vo.getReceiptDeptId (), 1 );
            vo.setReceiptCompanyName ( receiptCompanyName );
            //计算可下推付款退款单的金额=总价税合计-与应付单核销的金额
            List<KingPayableReceivableVerificationDetailEntity> list = iKingPayableReceivableVerificationDetailService.list ( new LambdaQueryWrapper<KingPayableReceivableVerificationDetailEntity> ().
                    eq ( KingPayableReceivableVerificationDetailEntity :: getDocNo, vo.getDocNo () ) );
            double sum = list.stream ().distinct ().mapToDouble ( verification -> Double.parseDouble ( verification.getVerificationAmount () ) ).sum ();
            if ( 0 >= ( Double.parseDouble ( vo.getActualMoney () ) - sum ) ) {
                log.info ( "付款单【" + vo.getDocNo () + "】已核销，不能下推。" );
            } else {
                vo.setCanDownMoney ( CommonUtil.subStr ( vo.getActualMoney (), String.valueOf ( sum ), 2 ) );
                payMenuVos.add ( vo );
            }
        } );
        Integer integer = kingFinancePayEntityMapper.queryCountMenu ( kingFinancePayEntity );
        PageParam pageParam = new PageParam ();
        pageParam.setRecords ( payMenuVos ).setTotal ( integer );
        return AjaxResult.success ( pageParam );
    }

    /**
     * 新增金蝶-财务-付款单，新增方式：
     * 1、手动新增
     * 2、应付单下推付款单
     * 3、其他应付单下推付款单
     * 4、工程下推付款单
     * @param kingFinancePayDto 金蝶-财务-付款单
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult draft ( KingFinancePayDto kingFinancePayDto ) {
        //处理基础公共部分
        KingFinancePayEntity payEntity = kingFinancePayDto.getKingFinancePayEntity ();
        payEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
        //payEntity.setDeptId ( CurrentUserUtil.getCompanyId () );
        payEntity.setCreateBy(StringUtils.isNotBlank(payEntity.getCreateBy()) ? payEntity.getCreateBy() : SecurityUtils.getLoginUser().getUser().getUserId());
        payEntity.setCreateTime ( DateUtils.getNowDate () );
        String py = ChineseToPinyin.getFirstLetter ( "付款单" );
        int count = this.count ();
        String countStr = String.format ( "%08d", count );
        payEntity.setDocNo ( py.concat ( countStr ) );
        //付款单明细，实际上是应付单，非应付单明细
        if ( payEntity.getSourceDocType () == KingConstUtil.PAY_TYPE_PAYABLE ) {
            checkPayable ( kingFinancePayDto.getKingFinancePayDetailEntities (), payEntity );
        } else if ( payEntity.getSourceDocType () == KingConstUtil.PAY_TYPE_OTHER_PAYABLE ) {
            checkPayableOther ( kingFinancePayDto.getKingFinancePayDetailEntities () );
        }
        //2.处理下是否有选择采购订单，如果有则绑定为采购订单的付款计划
        updateOrderPayPlan ( kingFinancePayDto.getKingFinancePayDetailEntities (), payEntity );
        //3.处理付款单明细
        updatePayDetail ( kingFinancePayDto.getKingFinancePayDetailEntities (), payEntity.getId () );
        //4.处理多收款人
        updatePayeeDetail (kingFinancePayDto.getKingFinancePayPayeesEntities (), payEntity.getId ());
        payEntity.setState ( ConstUtil.STATE_IN_DRAFT );
        boolean save = this.save ( payEntity );
        if ( ! save ) {
            return AjaxResult.error ( "暂存付款单基础数据失败" );
        }
        return AjaxResult.success ( "暂存付款单成功", payEntity.getId () );
    }

    /**
     * 新增金蝶-财务-付款单，新增方式：
     * 1、手动新增
     * 2、应付票据下推付款单，应付票据退票自动生成
     * 3、应付单下推付款单
     * 4、其他应付单下推付款单
     * 6、工程下推付款单
     *
     * @param payEntity 金蝶-财务-付款单
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult submit ( KingFinancePayEntity payEntity ) {
        //处理基础公共部分
        if ( StringUtils.isBlank ( payEntity.getId () ) ) {
            return AjaxResult.error ( "请先暂存该单据！" );
        }
        String bizName = "付款单_";
        StartProcessDto startDto = new StartProcessDto ().
                setBusinessId ( payEntity.getId () ).
                setBusinessType ( payEntity.getBusinessType () ).setModelId ( payEntity.getModelId () ).
                setBusinessName ( bizName.concat ( payEntity.getId () ) );
        String instanceId = iActModelService.startProcess ( startDto );
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinancePayEntity> ().
                set ( KingFinancePayEntity :: getInstanceId, instanceId ).
                set ( KingFinancePayEntity :: getState, ConstUtil.STATE_IN_PROCESS ).
                eq ( KingFinancePayEntity :: getId, payEntity.getId () ) );
        if ( ! update ) {
            return AjaxResult.error ( "提交付款单基础数据失败" );
        }
        return AjaxResult.success ( "提交付款单成功。" );
    }

    /**
     * 通过Id查询付款单的关联信息，推送金蝶
     *
     * @param id
     * @return
     */
    @Override
    public KingFinancePayEntity querySingleById (String id) {
        KingFinancePayEntity payEntity = kingFinancePayEntityMapper.querySingleById ( id );
        String transactionCompanyName = getCompanyName ( payEntity.getTransactionCompanyType (), payEntity.getTransactionCompanyId (), 1 );
        payEntity.setTransactionCompanyName ( transactionCompanyName );
        String receiptCompanyName = getCompanyName ( payEntity.getReceiptDeptType (), payEntity.getReceiptDeptId (), 1 );
        payEntity.setReceiptCompanyName ( receiptCompanyName );
        //处理收款单位和往来单位编码，送金蝶
        String receiptDeptCode = getCompanyName ( payEntity.getReceiptDeptType (), payEntity.getReceiptDeptId (), 2 );
        payEntity.setReceiptDeptCode ( receiptDeptCode );
        String transactionCompanyCode = getCompanyName ( payEntity.getTransactionCompanyType (), payEntity.getTransactionCompanyId (), 2 );
        payEntity.setTransactionCompanyCode ( transactionCompanyCode );
        return payEntity;
    }

    /**
     * 修改金蝶-财务-付款单
     *
     * @param kingFinancePayDto 金蝶-财务-付款单
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult updateKingFinancePayEntity ( KingFinancePayDto kingFinancePayDto ) {
        KingFinancePayEntity payEntity = kingFinancePayDto.getKingFinancePayEntity ();
        KingFinancePayEntity sourceEntity = this.getById ( payEntity );
        if ( sourceEntity.getState ().equals ( ConstUtil.STATE_FINISH ) ) {
            return AjaxResult.error ( "审批完成的单据无法操作！" );
        }
        payEntity.setUpdateTime ( DateUtils.getNowDate () );
        payEntity.setUpdateBy ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        boolean b = this.updateById ( payEntity );
        if ( ! b ) {
            throw new CustomException ( "修改付款单基本信息失败！" );
        }
        if ( payEntity.getSourceDocType () == KingConstUtil.PAY_TYPE_PAYABLE ) {
            checkPayable ( kingFinancePayDto.getKingFinancePayDetailEntities (), payEntity );
        } else if ( payEntity.getSourceDocType () == KingConstUtil.PAY_TYPE_OTHER_PAYABLE ) {
            checkPayableOther ( kingFinancePayDto.getKingFinancePayDetailEntities () );
        }
        //2.处理下是否有选择采购订单，如果有则绑定为采购订单的付款计划
        updateOrderPayPlan ( kingFinancePayDto.getKingFinancePayDetailEntities (), payEntity );
        //3.处理付款单明细
        updatePayDetail ( kingFinancePayDto.getKingFinancePayDetailEntities (), payEntity.getId () );
        //4.处理多收款人
        updatePayeeDetail (kingFinancePayDto.getKingFinancePayPayeesEntities (), payEntity.getId ());
        return AjaxResult.success ();
    }

    /**
     * 批量删除金蝶-财务-付款单
     *
     * @param ids 需要删除的金蝶-财务-付款单ID
     * @return 结果
     */
    @Override
    @Transactional ( rollbackFor = Exception.class, propagation = Propagation.REQUIRED )
    public AjaxResult deleteKingFinancePayEntityByIds ( String[] ids ) {
        LambdaUpdateWrapper<KingFinancePayEntity> payUpdate = new LambdaUpdateWrapper<> ();
        payUpdate.set ( KingFinancePayEntity :: getIsUsed, ConstUtil.IS_USED_NO ).in ( KingFinancePayEntity :: getId, ids );
        boolean update = this.update ( payUpdate );
        if ( ! update ) {
            throw new CustomException ( "删除付款单失败！" );
        }
        LambdaUpdateWrapper<KingFinancePayDetailEntity> detailUpdate = new LambdaUpdateWrapper<> ();
        detailUpdate.set ( KingFinancePayDetailEntity :: getIsUsed, ConstUtil.IS_USED_NO ).in ( KingFinancePayDetailEntity :: getRelationId, ids );
        boolean updateDetail = iKingFinancePayDetailService.update ( detailUpdate );
        if ( ! updateDetail ) {
            throw new CustomException ( "删除付款单失败！" );
        }
        return AjaxResult.success ( "删除付款单成功" );
    }

    /**
     * 审批后处理业务
     * 1、处理对应订单的本次付款金额
     * 2、核销金额
     * @param id
     * @return
     */
    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult approve (String id) {
        log.info ( "付款单审批后业务处理回调..." );
        KingFinancePayEntity payEntity = querySingleById ( id );
        if ( payEntity == null ) {
            throw new CustomException ( "该付款单不存在！" );
        }
        //1.处理付款单对应采购订单的付款金额和未付款金额
        updateOrderPayAmount ( payEntity );
        log.info ( "付款单更新对应采购订单付款金额成功。" );
        //2.核销对应应付单金额
        insertVerify ( payEntity );
        log.info ( "付款单更新对应应付单核销记录成功。" );
        //3.修改审批流状态
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinancePayEntity> ().set ( KingFinancePayEntity :: getState, ConstUtil.STATE_FINISH ).eq ( KingFinancePayEntity :: getId, id ) );
        if ( ! update ) {
            throw new CustomException ( "修改付款单审批流状态失败！" );
        }
        log.info ( "付款单更新审批流状态成功。" );
        //4.推送金蝶
        //payPushService.push ( payEntity );
        return AjaxResult.success ("付款单审核完成并成功推送金蝶。");
    }

    /**
     * 应付单下推付款单校验规则
     * 校验录入的付款单金额是否大于所选应付单金额
     *
     * @param payReceiptDetailEntities
     * @param payEntity
     */
    private void checkPayable ( List<KingFinancePayDetailEntity> payReceiptDetailEntities, KingFinancePayEntity payEntity ) {
        //财务应付的应付单
        if ( CollectionUtils.isEmpty ( payReceiptDetailEntities ) ) {
            return;
        }
        payReceiptDetailEntities = payReceiptDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceId () ) ).collect( Collectors.toList());
        List<String> ids = payReceiptDetailEntities.stream ().map ( KingFinancePayDetailEntity :: getSourceId ).collect ( Collectors.toList () );
        if ( CollectionUtils.isEmpty ( ids ) ) {
            return;
        }
        List<KingInvoicePayableEntity> list = iKingInvoicePayableService.list ( new LambdaQueryWrapper<KingInvoicePayableEntity> ().in ( KingInvoicePayableEntity :: getId, ids ) );
        boolean is = list.stream ().map ( KingInvoicePayableEntity :: getAccountType ).allMatch ( p -> p.equals ( "3" ) );
        if ( ! is ) {
            throw new CustomException ( "所选应付单的立账类型必须是财务应付！" );
        }
        //选择的多个应付单必须是同一个供应商
        boolean b = list.stream ().map ( KingInvoicePayableEntity :: getSupplierId ).allMatch ( p -> p.equals ( list.get ( 0 ).getSupplierId () ) );
        if ( ! b ) {
            throw new CustomException ( "分录行的应付单必须是同一个供应商！" );
        }
        //分录行的应付单必须和表头的供应商一致
        if ( !list.get ( 0 ).getSupplierId ().equals ( payEntity.getReceiptDeptId () ) ) {
            throw new CustomException ( "分录行的应付单必须和表头填写的供应商一致！" );
        }
        //付款单明细行《实付金额》不能大于源单（应付单）未结算金额=应付单的价税合计-已结算金额（应付单已下推付款单的实际金额）
        payReceiptDetailEntities.forEach ( detail -> {
            //1.付款单明细行《实付金额》
            double cope = Double.parseDouble ( detail.getActualMoney () );
            //2.应付单的价税合计
            double totalTaxPrice = 0;
            Optional<KingInvoicePayableEntity> first = list.stream ().filter ( payable -> payable.getId ().equals ( detail.getSourceId () ) ).findFirst ();
            if ( first.isPresent () ) {
                KingInvoicePayableEntity payableEntity = first.get ();
                totalTaxPrice = Double.parseDouble ( payableEntity.getTotalTaxPrice () );
            }
            //3.已结算金额（应付单已下推付款单的实际金额）
            double settMoney = 0;
            KingFinancePayDetailEntity one = iKingFinancePayDetailService.getOne ( new QueryWrapper<KingFinancePayDetailEntity> ().
                    select ( "SUM(actual_money) AS actualMoney" ).
                    eq ( "source_id", detail.getSourceId () ) );
            if ( one != null ) {
                settMoney = Double.parseDouble ( one.getActualMoney () );
            }
            if ( cope > ( totalTaxPrice - settMoney ) ) {
                throw new CustomException ( "第【" + detail.getLineNumber () + "】分录行下推实付金额超限！" );
            }
        } );
    }

    /**
     * 其他应付单下推，校验金额
     *
     * @param payReceiptDetailEntities
     * @param payReceiptDetailEntities
     */
    private void checkPayableOther ( List<KingFinancePayDetailEntity> payReceiptDetailEntities ) {
        //拿到所有其他应付单的明细
        if ( CollectionUtils.isEmpty ( payReceiptDetailEntities ) ) {
            return;
        }
        payReceiptDetailEntities = payReceiptDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceId () ) ).collect ( Collectors.toList () );
        List<String> ids = payReceiptDetailEntities.stream ().map ( KingFinancePayDetailEntity :: getSourceId ).collect ( Collectors.toList () );
        List<KingInvoicePayableReceivableOtherDetailEntity> payableOtherDetailEntities = iKingInvoicePayableReceivableOtherDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableReceivableOtherDetailEntity> ().in ( KingInvoicePayableReceivableOtherDetailEntity::getRelationOtherId, ids ) );
        for ( int i = 0; i < payableOtherDetailEntities.size (); i++ ) {
            //其他应付单明细
            KingInvoicePayableReceivableOtherDetailEntity payableOtherDetailEntity = payableOtherDetailEntities.get ( i );
            for ( int j = 0; j < payReceiptDetailEntities.size (); j++ ) {
                //付款单明细，由其他应付单明细生成，对比金额
                KingFinancePayDetailEntity payReceiptDetailEntity = payReceiptDetailEntities.get ( j );
                if ( payableOtherDetailEntity.getFeeCode ().equals ( payReceiptDetailEntity.getFeeCode () ) ) {
                    //同一个费用编码，表示同一行
                    if ( Double.parseDouble ( payReceiptDetailEntity.getActualMoney () ) > Double.parseDouble ( payableOtherDetailEntity.getTotalAmount () ) ) {
                        throw new CustomException ( "分录行的实付金额超限！" );
                    }
                }
            }
        }
    }

    /**
     * 如果付款单的明细里选择了：预付款+采购订单，则同步绑定该付款单为采购订单的付款计划
     * 检查采购订单是否属于表头的供应商
     *
     * @param payReceiptDetailEntities：付款单明细，拿有预付款的进行绑定采购订单付款计划
     * @param payEntity：付款单
     */
    private void updateOrderPayPlan ( List<KingFinancePayDetailEntity> payReceiptDetailEntities, KingFinancePayEntity payEntity ) {
        //筛选：预付款+采购订单的明细
        List<KingFinancePayDetailEntity> collect = payReceiptDetailEntities.stream ().filter ( d -> StringUtils.isNotBlank ( d.getOrderId () ) ).collect ( Collectors.toList () );
        log.info ( "选择了采购订单的-> {}", collect );
        if ( CollectionUtils.isEmpty ( collect ) ) {
            return;
        }
        //检查所有行的采购订单Id是否一致
//        boolean b = collect.stream ().map ( KingFinancePayDetailEntity :: getOrderId ).allMatch ( prop -> prop.equals ( collect.get ( 0 ).getOrderId () ) );
//        if ( ! b ) {
//            throw new CustomException ( "分录行的采购订单必须是同一个供应商！" );
//        }
        OrderEntity orderEntity = iOrderService.getById ( collect.get ( 0 ).getOrderId () );
        if ( ! orderEntity.getSupplierId ().equals ( payEntity.getReceiptDeptId () ) ) {
            throw new CustomException ( "分录行的采购订单必须和表头填写的供应商一致！" );
        }
        //保存采购订单的付款计划
        if ( ! CollectionUtils.isEmpty ( collect ) ) {
            iOrderPaymentPlanService.remove ( new LambdaUpdateWrapper<OrderPaymentPlanEntity> ().eq ( OrderPaymentPlanEntity :: getPrepaidCode, payEntity.getDocNo () ) );
            payReceiptDetailEntities.forEach ( detail -> {
                /**
                 * 校验订单预付金额：
                 * 1、预付比例=预付金额/订单价税合计总金额
                 * 2、预付金额 不能大于 该笔订单未付款金额=订单价税合计-实际预付金额
                 */
                String totalUnpaidAmount = StringUtils.isBlank ( orderEntity.getTotalUnpaidAmount () ) ? "0" : orderEntity.getTotalUnpaidAmount ();//采购订单累计未付款金额
                String totalPriceTax = StringUtils.isBlank ( orderEntity.getTotalPriceTax () ) ? "0" : orderEntity.getTotalPriceTax ();//采购订单价税合计金额
                if ( StringUtils.isBlank ( detail.getCopeMoney () ) ) {
                    throw new CustomException ( "【" + detail.getOrderCode () + "】的应付金额不能为空！" );
                }
                if ( Double.parseDouble ( detail.getCopeMoney () ) > Double.parseDouble ( totalUnpaidAmount ) ) {
                    throw new CustomException ( "【" + detail.getOrderCode () + "】的应付金额不能超过未付款金额【" + totalUnpaidAmount + "】元！" );
                }
                OrderPaymentPlanEntity orderPaymentPlanEntity = new OrderPaymentPlanEntity ();
                orderPaymentPlanEntity.setOrderId ( detail.getOrderId () ).
                        setPayableMoney ( detail.getCopeMoney () ).
                        setPayableRate ( CommonUtil.divisionStr ( detail.getCopeMoney (), totalPriceTax, 5 ) ).
                        setIsPrepaid ( true ).
                        setPrepaidCode ( payEntity.getDocNo () ).
                        setActualPrepaidAmount ( detail.getCopeMoney () );
                int i = iOrderPaymentPlanService.insertOrderPaymentPlanEntity ( orderPaymentPlanEntity );
                if ( i == 0 ) {
                    throw new CustomException ( "绑定预付款单与采购订单的付款计划失败！" );
                }
            } );
        }
    }

    /**
     * 处理付款单的明细
     * 当付款明细行结算方式为电汇，校验我方银行账号的余额够不够付款
     * @param kingFinancePayReceiptDetailEntities
     * @param relationId
     */
    private void updatePayDetail ( List<KingFinancePayDetailEntity> kingFinancePayReceiptDetailEntities, String relationId ) {
        if ( ! CollectionUtils.isEmpty ( kingFinancePayReceiptDetailEntities ) ) {
            iKingFinancePayDetailService.remove ( new LambdaUpdateWrapper<KingFinancePayDetailEntity> ().eq ( KingFinancePayDetailEntity :: getRelationId, relationId ) );
            kingFinancePayReceiptDetailEntities.forEach ( payDetail -> {
                if ( payDetail.getKingSettlementTypeCode ().equals ( KingConstUtil.SETT_TYPE_DH ) ) {
                    KingBankAccountEntity one = iKingBankAccountService.getOne ( new LambdaQueryWrapper<KingBankAccountEntity> ().eq ( KingBankAccountEntity :: getAccount, payDetail.getBankNumber () ) );
                    if ( Double.parseDouble ( StringUtils.isBlank ( one.getOpeningBalance () ) ? "0" : one.getOpeningBalance () ) < Double.parseDouble ( StringUtils.isBlank ( payDetail.getActualMoney () ) ? "0" : payDetail.getActualMoney () ) ) {
                        throw new CustomException ( "【" + payDetail.getBankNumber () + "】的账户余额不足以支付" + payDetail.getActualMoney () + "元。" );
                    }
                }
                payDetail.setRelationId ( relationId );
                payDetail.setCreateBy(StringUtils.isNoneBlank(payDetail.getCreateBy()) ? payDetail.getCreateBy() : SecurityUtils.getLoginUser().getUser().getUserId());
                payDetail.setCreateTime ( DateUtils.getNowDate () );
                iKingFinancePayDetailService.insertKingFinancePayReceiptDetailEntity ( payDetail );
            } );
        }
    }

    /**
     * 处理多收款人列表
     * @param kingFinancePayPayeesEntities
     * @param relationId
     */
    private void updatePayeeDetail (List<KingFinancePayPayeesEntity> kingFinancePayPayeesEntities, String relationId) {
        if ( ! CollectionUtils.isEmpty ( kingFinancePayPayeesEntities ) ) {
            iKingFinancePayPayeesService.remove ( new LambdaUpdateWrapper<KingFinancePayPayeesEntity> ().eq ( KingFinancePayPayeesEntity :: getPayId, relationId ) );
            kingFinancePayPayeesEntities.forEach ( payee -> {
                payee.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                payee.setPayId ( relationId );
                payee.setCreateBy ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
                payee.setCreateTime ( DateUtils.getNowDate () );
                boolean save = iKingFinancePayPayeesService.save ( payee );
                if ( ! save ) {
                    throw new CustomException ( "更新多处理人失败！" );
                }
            } );
        }
    }

    /**
     * 更新付款单对应采购订单的付款金额
     * @param payEntity
     */
    private void updateOrderPayAmount (KingFinancePayEntity payEntity) {
        List<KingFinancePayDetailEntity> list = iKingFinancePayDetailService.list ( new LambdaQueryWrapper<KingFinancePayDetailEntity> ().
                eq ( KingFinancePayDetailEntity :: getRelationId, payEntity.getId () ) );
        //所有明细
        list.forEach ( detail -> {
            String orderId = detail.getOrderId ();
            if ( StringUtils.isNotBlank ( orderId ) ) {
                //将该订单的未付款金额和已付款金额重新计算
                OrderEntity orderEntity = iOrderService.getById ( orderId );
                String totalUnpaidAmount = orderEntity.getTotalUnpaidAmount (); //订单未付款金额
                String totalPaymentAmount = orderEntity.getTotalPaymentAmount (); //累计付款金额
                //未付款金额减少，累计付款金额增加，数值为本次付款总金额
                String actualMoney = payEntity.getActualMoney (); //实付金额
                //累计付款金额
                orderEntity.setTotalPaymentAmount ( CommonUtil.sumStr ( totalPaymentAmount, actualMoney, 5 ) ).
                        setTotalUnpaidAmount ( CommonUtil.subStr ( totalUnpaidAmount, actualMoney, 5 ) );
                boolean b = iOrderService.updateById ( orderEntity );
                if ( ! b ) {
                    throw new CustomException ( "更新订单累计付款金额失败！" );
                }
            }
        } );
    }

    /**
     * 付款单审批成功后，添加自动核销与匹配核销核销单
     * 1、自动核销：检查付款单的源单类型是否为财务应付单，如果是则进行自动核销
     * 2、匹配核销：检查付款单内是否有采购订单号，如果有则表示为预付款，则进行匹配核销
     * 3、手动核销：源单既不是应付单，又没有采购订单，就是手动核销
     * @param payEntity
     * @return
     */
    private AjaxResult insertVerify ( KingFinancePayEntity payEntity ) {
        //源单类型是应付单，自动核销，根据付款单明细行的sourceId找打应付单记录
        List<KingFinancePayDetailEntity> list = iKingFinancePayDetailService.list ( new LambdaQueryWrapper<KingFinancePayDetailEntity> ().
                eq ( KingFinancePayDetailEntity :: getRelationId, payEntity.getId () ) );
        //通过付款单明细找到上级单据，与上级应付单进行核销
        for ( KingFinancePayDetailEntity payDetail : list ) {
            if ( payEntity.getSourceDocType () == KingConstUtil.PAY_TYPE_PAYABLE ) {
                //1.自动核销
                KingInvoicePayableEntity payableEntity = iKingInvoicePayableService.querySingleById ( payDetail.getSourceId () );
                if ( ! ObjectUtils.isEmpty ( payableEntity ) ) {
                    //判断是否同一供应商（应付单的供应商，付款单的收款单位），同一币别，同一结算组织
                    if ( "BD_Supplier".equals ( payEntity.getReceiptDeptType () ) &&
                            payEntity.getReceiptDeptId ().equals ( payableEntity.getSupplierId () ) &&
                            payEntity.getKingBaseCurrencyCode ().equals ( payableEntity.getCurrencyCode () ) &&
                            payEntity.getSettlementDeptId ().equals ( payableEntity.getSettlementDeptId () ) ){
                        //1.核销基础对象
                        KingPayableReceivableVerificationEntity payableVerificationEntity = new KingPayableReceivableVerificationEntity ();
                        SysDept sysDept = iSysDeptService.getById ( payEntity.getSettlementDeptId () );
                        KingBaseCurrencyEntity one = iKingBaseCurrencyService.getOne ( new LambdaQueryWrapper<KingBaseCurrencyEntity> ().eq ( KingBaseCurrencyEntity :: getCode, sysDept.getStandardCurrencyCode () ) );
                        payableVerificationEntity.setSettlementCompany ( sysDept.getDeptName () );
                        payableVerificationEntity.setOwnCurrencyTitle ( one.getCurrencyName () );
                        payableVerificationEntity.setPayReceiptCompany ( sysDept.getDeptName () );
                        payableVerificationEntity.setVerificationType ( 1 );
                        payableVerificationEntity.setType ( 1 );
                        //2.核销列表对象
                        //核销金额按照先后顺序，比如付款单总的10000，应付单三个明细分别为1000,5000,8000，那么就按照先后顺序，先核销1000,5000,4000
                        String amount = Double.parseDouble ( payEntity.getActualMoney () ) > Double.parseDouble ( payableEntity.getTotalTaxPrice () ) ? payableEntity.getTotalTaxPrice () : payEntity.getActualMoney ();
                        KingPayableReceivableVerificationDetailEntity payVerification = new KingPayableReceivableVerificationDetailEntity ();
                        payVerification.setDoc ( "付款单" ).setDocType ( payEntity.getDocTypeCode () ).setDocNo ( payEntity.getDocNo () ).setLineNumber ( payDetail.getLineNumber () ).
                                setBizTime ( payEntity.getBusinessTime () ).setTransactionDeptTitle ( payEntity.getTransactionCompanyName () ).setCurrencyTitle ( payEntity.getSettlementCurrencyName () ).
                                setRate ( payEntity.getRate () ).setVerificationAmount ( amount ).setPayPurpose ( payDetail.getPayReceiptPurposeCode () ).setMaterialName ( payDetail.getMaterialCode () ).setFeeItem ( payDetail.getFeeCode () );
                        KingPayableReceivableVerificationDetailEntity payableVerification = new KingPayableReceivableVerificationDetailEntity ();
                        payableVerification.setDoc ( "应付单" ).setDocType ( payableEntity.getDocTypeCode () ).setDocNo ( payableEntity.getDocNo () ).setLineNumber ( "1" ).setBizTime ( payableEntity.getBizTime () ).
                                setTransactionDeptTitle ( payableEntity.getSupplierName () ).setCurrencyTitle ( payableEntity.getCurrencyName () ).setRate ( payableEntity.getRate () ).setVerificationAmount ( amount );
                        List<KingPayableReceivableVerificationDetailEntity> payableVerificationDetailEntities = new ArrayList<> ();
                        payableVerificationDetailEntities.add ( payVerification );
                        payableVerificationDetailEntities.add ( payableVerification );
                        iKingPayableReceivableVerificationService.saveSingle ( payableVerificationEntity, payableVerificationDetailEntities );
                    }
                }
            } else if ( StringUtils.isNotBlank ( payDetail.getOrderId () ) ) {
                //2.匹配核销：直接新增付款单，付款单选中采购订单，生成的付款单，通过此采购订单下推的财务应付单，按照时间先后顺序，依次【匹配核销】，生成应付核销单，单据类型是【匹配核销】。
                //系统付款条件为按金额预付的时候，一个单据的核销按照单个物料占总个单据的占比去核销。
                List<String> orderIds = list.stream ().map ( KingFinancePayDetailEntity :: getOrderId ).distinct ().collect ( Collectors.toList () );
                iKingPayableReceivableVerificationService.matchVerify ( 1, 2, payEntity.getId (), orderIds );
            }
        }
        return AjaxResult.success ( "自动核销单生成成功" );
    }

    /**
     * 根据往来单位类型，获取往来单位和付款单位
     *
     * @param companyType：单位类型，根据往来单位或付款单位的类型魔法值
     * @param companyId：单位Id，根据单位类型，选择的具体单位Id
     * @param type：标志位，根据其判断要查询名称还是编码，1：查询名称；2：查询编码
     * @return
     */
    private String getCompanyName ( String companyType, String companyId, Integer type ) {
        if ( StringUtils.isBlank ( companyType ) || StringUtils.isBlank ( companyId ) ) {
            return "";
        }
        String transactionCompany = "";
        switch ( companyType ) {
            case "BD_Customer":
                CustomerEntity customerEntity = iCustomerService.getById ( companyId );
                if ( customerEntity != null && type == 1 )
                    transactionCompany = customerEntity.getCustomerName ();
                else if ( customerEntity != null && type == 2 )
                    transactionCompany = customerEntity.getCustomerCode ();
                break;
            case "BD_Supplier":
                SupplierEntity supplierEntity = iSupplierService.getById ( companyId );
                if ( supplierEntity != null && type == 1  )
                    transactionCompany = supplierEntity.getSupplierName ();
                else if ( supplierEntity != null && type == 2 )
                    transactionCompany = supplierEntity.getSupplierCode ();
                break;
            case "BD_Department":
                SysDept sysDept = iSysDeptService.getById ( companyId );
                if ( sysDept != null && type == 1  )
                    transactionCompany = sysDept.getDeptName ();
                else if ( sysDept != null && type == 2 )
                    transactionCompany = sysDept.getDeptCode ();
                break;
            case "BD_Empinfo":
                SysUser sysUser = iSysUserService.getById ( companyId );
                if ( sysUser != null && type == 1  )
                    transactionCompany = sysUser.getNickName ();
                else if ( sysUser != null && type == 2 )
                    transactionCompany = sysUser.getUserCode ();
                break;
            case "FIN_OTHERS":
                KingBaseTransactionsCompanyEntity transactionsCompanyEntity = iKingBaseTransactionsCompanyService.getById ( companyId );
                if ( transactionsCompanyEntity != null && type == 1  )
                    transactionCompany = transactionsCompanyEntity.getTitle ();
                else if ( transactionsCompanyEntity != null && type == 2 )
                    transactionCompany = transactionsCompanyEntity.getCompanyCode ();
                break;
            case "ORG_Organizations":
                SysDept organ = iSysDeptService.getById ( companyId );
                if ( organ != null && type == 1  )
                    transactionCompany = organ.getDeptName ();
                else if ( organ != null && type == 2 )
                    transactionCompany = organ.getDeptCode ();
                break;
            case "BD_BANK":
                KingBaseBankEntity bankEntity = iKingBaseBankService.getById ( companyId );
                if ( bankEntity != null && type == 1  )
                    transactionCompany = bankEntity.getTitle ();
                else if ( bankEntity != null && type == 2 )
                    transactionCompany = bankEntity.getCode ();
                break;
        }
        return transactionCompany;
    }

}
